<?php /** @noinspection PhpDocMissingThrowsInspection */

/** @noinspection PhpUnhandledExceptionInspection */

namespace DreamCat\FrameCore\Cases\Controller;

use DreamCat\Array2Class\Array2ClassConverter;
use DreamCat\FrameCore\Factory\Impl\ActionParam\DefaultActionParamFactory;
use DreamCat\FrameCore\HelperClass\Controller\DemoController;
use DreamCat\FrameCore\HelperClass\Factory\ServerRequestFactory;
use DreamCat\FrameCore\HelperClass\Vo\DemoVo;
use PHPUnit\Framework\TestCase;
use Psr\Http\Message\ServerRequestInterface;
use RuntimeException;

/**
 * 测试控制器
 * @author vijay
 */
class ControllerTest extends TestCase
{
    /**
     * 测试控制器方法的参数解析
     * @param string $fnName 控制器方法
     * @param array $pathVariables 占位符列表
     * @param ServerRequestInterface $request 请求数据
     * @param array $expect 期望的参数列表
     * @return void
     * @throws \ReflectionException
     * @dataProvider paramParseData
     */
    public function testParamParse(string $fnName, array $pathVariables, ServerRequestInterface $request, array $expect)
    {
        $ref = new \ReflectionMethod(DemoController::class, $fnName);
        $result = $this->getActionParamFactory()->createParams($ref, $request, $pathVariables);
        static::assertEquals($expect, $result, "方法{$fnName}参数解析不正确");
    }

    /**
     * 生成控制器方法参数生成器
     * @return DefaultActionParamFactory 控制器方法参数生成器
     */
    private function getActionParamFactory()
    {
        $parser = new DefaultActionParamFactory();
        $parser->setArray2Class(new Array2ClassConverter());
        return $parser;
    }

    /**
     * 测试复杂vo无法赋值的情况
     * @return void
     * @throws \ReflectionException
     */
    public function testComplexVO()
    {
        $this->expectExceptionMessage("无法构建 " . DemoController::class . "::testComplexVo::vo 参数");
        $this->expectException(RuntimeException::class);
        $ref = new \ReflectionMethod(DemoController::class, "testComplexVo");
        $this->getActionParamFactory()->createParams($ref, ServerRequestFactory::createByPostJson("", "", []), []);
    }

    public function testCantParseBody()
    {
        $contentType = "application/unknown";
        $this->expectExceptionMessage("无法解析消息体, type = {$contentType}");
        $this->expectException(RuntimeException::class);
        $ref = new \ReflectionMethod(DemoController::class, "testDemoVo");
        $request = ServerRequestFactory::createByPostJson("", ["ab", "cc"], []);
        $request = $request->withHeader("Content-Type", $contentType);
        $this->getActionParamFactory()->createParams($ref, $request, []);
    }

    /**
     * 测试参数类型为callable的情况
     * @return void
     * @throws \ReflectionException
     */
    public function testParamCallback()
    {
        $ref = new \ReflectionMethod(DemoController::class, "testBodyCall");
        $str = uniqid("call-");
        $request = ServerRequestFactory::createByPostJson("", ["c" => $str], []);
        $result = $this->getActionParamFactory()->createParams($ref, $request, []);
        static::assertEquals(1, count($result), "参数个数不正确");
        static::assertTrue(is_callable($result[0]), "参数类型不正确");
        static::assertTrue($str === $result[0](), "参数返回不正确");
    }

    /**
     * 测试参数解析的数据供给器
     * @return array
     */
    public function paramParseData()
    {
        return [
            [
                "demo",
                ["label" => "ruiming"],
                ServerRequestFactory::createByPostJson("", ["htes" => 5], ["page" => 5]),
                [
                    "ruiming",
                    5,
                    ["htes" => 5],
                    ["label" => "ruiming"],
                    10,
                ],
            ],
            [
                "index",
                ["autoPath" => "value1"],
                ServerRequestFactory::createByPostJson(
                    "",
                    ["content" => "json"],
                    [
                        "p" => "pp",
                        "p2" => 3,
                    ]
                ),
                [
                    "pp",
                    3,
                    "{\"content\":\"json\"}",
                    "value1",
                    [
                        "p" => "pp",
                        "p2" => 3,
                    ],
                ],
            ],
            [
                "testPost",
                [],
                ServerRequestFactory::createByPostForm(
                    "",
                    ["field" => "form1"],
                    [],
                    "test.domain.com",
                    false
                ),
                [
                    "form1",
                    null,
                    0,
                    "",
                ],
            ],
            [
                "testPost",
                [],
                ServerRequestFactory::createByPostForm(
                    "",
                    ["field" => "form1"],
                    []
                ),
                [
                    "form1",
                    null,
                    0,
                    "",
                ],
            ],
            [
                "testPost",
                [],
                ServerRequestFactory::createByPostJson(
                    "",
                    ["field" => "form2"],
                    []
                ),
                [
                    "form2",
                    null,
                    0,
                    "",
                ],
            ],
            [
                "testBodyField",
                [],
                ServerRequestFactory::createByPostJson(
                    "",
                    [
                        "s" => "f",
                        "i" => "5",
                        "f" => "3.2",
                        "b" => "1",
                        "a" => "val",
                    ],
                    []
                ),
                [
                    "f",
                    5,
                    3.2,
                    true,
                    ["val"],
                ],
            ],
            [
                "testNotStdClass",
                [],
                ServerRequestFactory::createByPostJson(
                    "",
                    ["c" => "f"],
                    []
                ),
                [new \stdClass()],
            ],
            [
                "testDemoVo",
                [],
                ServerRequestFactory::createByPostJson(
                    "",
                    ["page" => "3"],
                    []
                ),
                [new DemoVo(3)],
            ],
            [
                "testStd",
                ["size" => 3],
                ServerRequestFactory::createByPostJson("", ["htes" => 5], ["page" => 5]),
                [
                    (object)["htes" => 5],
                    new DemoVo(5),
                    new DemoVo(1, 3),
                ],
            ],
            [
                "testNotStdClass",
                [],
                ServerRequestFactory::createByPostJson(
                    "",
                    "",
                    []
                ),
                [new \stdClass()],
            ],
            [
                "testNotStdClass",
                [],
                ServerRequestFactory::createByPostJson(
                    "",
                    ["d" => "f"],
                    []
                ),
                [new \stdClass()],
            ],
        ];
    }

    /**
     * @param string $fnName 控制器方法
     * @param array $pathVariables 占位符列表
     * @param ServerRequestInterface $request 请求数据
     * @param string $errorClass 异常类
     * @param string $errorMsg 异常信息
     * @param int $errorCode 异常码
     * @return void
     * @throws \ReflectionException
     * @dataProvider paramParseErrorData
     */
    public function testParamParseError(
        string $fnName,
        array $pathVariables,
        ServerRequestInterface $request,
        string $errorClass,
        string $errorMsg,
        int $errorCode
    ) {
        $this->expectException($errorClass);
        $this->expectExceptionMessage($errorMsg);
        $this->expectExceptionCode($errorCode);
        $ref = new \ReflectionMethod(DemoController::class, $fnName);
        $this->getActionParamFactory()->createParams($ref, $request, $pathVariables);
    }

    /**
     * 解析异常的数据供给器
     * @return array 测试数据
     */
    public function paramParseErrorData()
    {
        return [
            [
                "testPost",
                [],
                ServerRequestFactory::createByPostJson(
                    "",
                    "{\"content:\"json\"}",
                    []
                ),
                RuntimeException::class,
                "json decode error: Syntax error",
                4,
            ],
        ];
    }
}

# end of file
